home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 7
/
Apprentice-Release7.iso
/
Source Code
/
Pascal
/
Snippets
/
PNL Libraries
/
MyEditObject.p
< prev
next >
Wrap
Text File
|
1996-06-01
|
17KB
|
633 lines
unit MyEditObject;
interface
uses
Dialogs;
type
EditObject = object
window: DialogPtr;
titem: integer;
vcontrol, hcontrol: ControlHandle;
te: TEHandle;
titemr: Rect;
hasgrow, drawgrow: boolean; { hasgrow -> leave room for grow icon, drawgrow -> draw it during updates }
doubleClickTime, tripleClickTime: longint;
readonly: boolean;
undotext: Handle;
undostart, undoend, undoselstart, undoselend: integer;
undoopen: boolean;
modified: boolean;
procedure Create (dlg: DialogPtr; item, width: integer; vscroll, hscroll, hasgrowb, drawgrowb, static: boolean);
procedure Destroy;
procedure Adjust;
procedure Resize;
procedure Draw;
function EditMenuEnabled: boolean;
procedure SetEditMenuItem (item: integer);
procedure DoEditMenu (item: integer);
procedure DoItemWhere (er: EventRecord; item: integer);
procedure DoIdle;
procedure DoKey (modifiers: integer; ch: char);
procedure DoActivateDeactivate (activate: boolean);
procedure ClickLoop;
procedure Click (pt: Point; extend: boolean);
function WordBreak (text: Ptr; pos: integer; forward: boolean): boolean;
end;
implementation
uses
TextEdit, Scrap, MyMenus, MyOOMainLoop, BaseGlobals, MyTypes, MyUtils, MySystemGlobals;
var
teo: EditObject;
teOriginalClickLoop: ProcPtr;
{ DON'T EVEN THINK ABOUT LOOKING AT THIS CODE!!!!! }
procedure CallCL (addr: ProcPtr);
inline
$205F, $4E90;
procedure SetD0to1;
inline
$7001;
function GetD2: longint;
inline
$2F42, $0000;
procedure Unlink;
inline
$4E5E;
procedure Link;
inline
$4E56, $0000;
{$PUSH}
{$D-}
{ Turn debug off, lest our qute little SetD0to1 hack gets crunged by TP }
procedure CallClickLoop; { There must be a better way to sort out this crap! }
begin
Unlink; { This is a rediculous hack! }
CallCL(teOriginalClickLoop);
Link;
teo.ClickLoop;
SetD0to1;
end;
function CallWordBreak (text: Ptr; pos: integer): boolean;
var
d2: longint;
begin
d2 := GetD2;
CallWordBreak := teo.WordBreak(text, pos, BAND(d2, $00020000) = 0);
end;
{$POP}
function FindEOL (te: TEHandle; loc: integer): integer;
begin
while (loc < te^^.teLength) and (Ptr(longint(te^^.hText^) + loc)^ <> 13) do
loc := loc + 1;
FindEOL := loc;
end;
procedure EditObject.Click (pt: Point; extend: boolean);
var
tc, dct: longint;
doubleclick, tripleclick: boolean;
teOriginalWordBreak: ProcPtr;
eol: integer;
begin
SetPort(window);
tc := TickCount;
doubleclick := tc < doubleClickTime;
tripleclick := tc < tripleClickTime;
teo := self;
teOriginalClickLoop := te^^.clickLoop;
te^^.clickLoop := @CallClickLoop;
teOriginalWordBreak := te^^.wordBreak;
if tripleclick then
SetWordBreak(@CallWordBreak, te);
if extend and tripleclick then begin{ we must fake text edit into not shrinking the selection somehow }
eol := FindEOL(te, te^^.selStart); { if start<=clickloc<=EOL(start)<selEnd }
if (te^^.selStart <= te^^.clickloc) and (te^^.clickloc <= eol) and (eol < te^^.selEnd) then
TESetSelect(te^^.clickloc, te^^.selEnd, te);
end;
TEClick(pt, extend, te);
tc := TickCount;
dct := GetDblTime;
doubleClickTime := tc + dct;
if doubleclick then
tripleClickTime := tc + dct;
te^^.clickLoop := teOriginalClickLoop;
te^^.wordBreak := teOriginalWordBreak;
if readonly and (te^^.selStart = te^^.selEnd) then begin { kludge to make the carret go away }
TEDeactivate(te);
TEActivate(te);
end;
end;
function DirtyKey (ch: char): boolean;
begin
case ord(ch) of
homeChar, endChar, helpChar, pageUpChar, pageDownChar, leftArrowChar, rightArrowChar, upArrowChar, downArrowChar:
DirtyKey := false;
otherwise
DirtyKey := true;
end;
end;
procedure EditObject.Create (dlg: DialogPtr; item, width: integer; vscroll, hscroll, hasgrowb, drawgrowb, static: boolean);
var
dr, vr: Rect;
k:integer;
h:Handle;
begin
readonly := static;
doubleClickTime := -1;
tripleClickTime := -1;
SetPort(dlg);
window := dlg;
titem := item;
hasgrow := hasgrowb;
drawgrow := drawgrowb;
if vscroll then begin
SetRect(dr, 0, 0, 16, 100);
vcontrol := NewControl(window, dr, '', true, 0, 0, 0, scrollBarProc, 0);
end
else
vcontrol := nil;
if hscroll then begin
SetRect(dr, 0, 0, 100, 16);
hcontrol := NewControl(window, dr, '', true, 0, 0, 0, scrollBarProc, 0);
end
else
hcontrol := nil;
GetDialogItem(dlg, titem, k, h, dr);
titemr := dr;
EraseRect(dr);
vr := dr;
dr.right := dr.left + width;
te := TENew(dr, vr);
TEAutoView(true, te);
undotext := NewHandle(0);
undostart := -1;
undoopen := false;
modified := false;
Resize;
end;
procedure EditObject.Destroy;
begin
TEDispose(te);
DisposeHandle(undotext);
dispose(self);
end;
procedure AdjustTE (te: TEHandle; hc, vc: integer);
{Scroll the TERec around to match up to the potentially updated scrollbar}
{values. This is really useful when the window resizes such that the}
{scrollbars become inactive and the TERec had been previously scrolled.}
begin
with te^^ do
TEScroll((viewRect.left - destRect.left) - hc, (viewRect.top - destRect.top) - (vc * lineHeight), te);
end; {AdjustTE}
function AdjustHV (isVert: BOOLEAN; control: ControlHandle; te: TEHandle; canRedraw: BOOLEAN): integer;
{Calculate the new control maximum value and current value, whether it is the horizontal or}
{vertical scrollbar. The vertical max is calculated by comparing the number of lines to the}
{vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document}
{width to the width of the viewRect. The current values are set by comparing the offset between}
{the view and destination rects. If necessary and we canRedraw, have the control be re-drawn by}
{calling ShowControl.}
var
value, lines, max: INTEGER;
oldValue, oldMax: INTEGER;
cliprgn: RgnHandle;
r: Rect;
begin
oldValue := GetControlValue(control);
oldMax := GetControlMaximum(control);
with te^^ do begin
if isVert then begin
lines := nLines;
{since nLines isn’t right if the last character is a return, check for that case}
if (teLength > 0) & (Ptr(ORD(hText^) + teLength - 1)^ = 13) then
lines := lines + 1;
max := lines - ((viewRect.bottom - viewRect.top) div lineHeight);
end
else
max := destRect.right - destRect.left - (viewRect.right - viewRect.left);
if max < 0 then
max := 0; {check for negative values}
if isVert then
value := (viewRect.top - destRect.top) div lineHeight
else
value := viewRect.left - destRect.left;
if value < 0 then
value := 0
else if value > max then
value := max; {pin the value to within range}
end;
SetPort(te^^.inPort);
cliprgn := NewRgn;
GetClip(clipRgn);
SetRect(r, 0, 0, 0, 0);
ClipRect(r);
SetControlMaximum(control, max);
SetClip(clipRgn);
DisposeRgn(clipRgn);
SetControlValue(control, value);
if canRedraw and ((max <> oldMax) or (value <> oldValue)) then
ShowControl(control); {check to see if the control can be re-drawn}
AdjustHV := value;
end; {AdjustHV}
procedure EditObject.Adjust;
var
hc, vc: integer;
begin
vc := AdjustHV(true, vcontrol, te, false);
hc := AdjustHV(false, hcontrol, te, false);
AdjustTE(te, hc, vc);
end; {AdjustScrollValues}
procedure EditObject.Resize;
const
invis = 0;
vis = 255;
inset = 3;
var
vr, tr: Rect;
pt: Point;
k: integer;
h: Handle;
ht: integer;
hc, vc: integer;
begin
SetPort(window);
EraseRect(titemr);
GetDialogItem(window, titem, k, h, tr);
titemr := tr;
InvalRect(tr);
vr := tr;
InsetRect(vr, inset, inset);
if hcontrol <> nil then
vr.bottom := vr.bottom - 15;
if vcontrol <> nil then
vr.right := vr.right - 15;
vr.bottom := vr.top + (vr.bottom - vr.top) div te^^.lineHeight * te^^.lineHeight;
pt := vr.topLeft;
SubPt(te^^.viewRect.topLeft, pt);
OffsetRect(te^^.destRect, pt.h, pt.v);
te^^.viewRect := vr;
if vcontrol <> nil then begin
vcontrol^^.contrlVis := invis;
MoveControl(vcontrol, tr.right - 16, tr.top);
ht := tr.bottom - tr.top;
if hasgrow then
ht := ht - 15;
SizeControl(vcontrol, 16, ht);
vc := AdjustHV(true, vcontrol, te, false);
vcontrol^^.contrlVis := vis;
end;
if hcontrol <> nil then begin
hcontrol^^.contrlVis := invis;
MoveControl(hcontrol, tr.left, tr.bottom - 16);
ht := tr.right - tr.left;
if hasgrow or (vcontrol <> nil) then
ht := ht - 15;
SizeControl(hcontrol, ht, 16);
hc := AdjustHV(false, hcontrol, te, false);
hcontrol^^.contrlVis := vis;
end;
AdjustTE(te, hc, vc);
end;
procedure EditObject.Draw;
var
r: Rect;
k: integer;
h: Handle;
begin
GetDialogItem(window, titem, k, h, r);
EraseRect(r);
if drawgrow then begin
DrawGrowIcon(window);
end;
if vcontrol <> nil then begin
Draw1Control(vcontrol);
end;
if hcontrol <> nil then begin
Draw1Control(hcontrol);
end;
EraseRect(te^^.viewRect);
TEUpdate(te^^.viewRect, te);
end;
procedure EditObject.DoActivateDeactivate (activate: boolean);
begin
if drawgrow then
DrawGrowIcon(window);
if activate then
TEActivate(te)
else
TEDeactivate(te);
end;
{ Common algorithm for pinning the value of a control. It returns the actual amount }
{ the value of the control changed. }
procedure CommonAction (control: ControlHandle; var amount: integer);
var
value, max: integer;
begin
value := GetControlValue(control);
max := GetControlMaximum(control);
amount := value - amount;
if (amount <= 0) then
amount := 0
else if (amount >= max) then
amount := max;
SetControlValue(control, amount);
amount := value - amount; { calculate true change }
end; { CommonAction }
var
actionTE: TEHandle;
{ Determines how much to change the value of the vertical scrollbar by and how }
{ much to scroll the TE record.}
procedure VActionProc (control: ControlHandle; part: integer);
var
amount: integer;
window: WindowPtr;
begin
if (part <> 0) then begin
window := control^^.contrlOwner;
case part of
kInUpButtonControlPart, kInDownButtonControlPart: { one line }
amount := 1;
kInPageUpControlPart, kInPageDownControlPart: { one page }
with actionTE^^, viewRect do
amount := (bottom - top) div lineHeight;
end;
if ((part = kInDownButtonControlPart) or (part = kInPageDownControlPart)) then
amount := -amount; { reverse direction for a downer }
CommonAction(control, amount);
if (amount <> 0) then
TEScroll(0, amount * actionTE^^.lineHeight, actionTE);
end;
end; { VActionProc }
{ Determines how much to change the value of the horizontal scrollbar by and how }
{ much to scroll the TE record. }
procedure HActionProc (control: ControlHandle; part: integer);
var
amount: integer;
window: WindowPtr;
begin
if (part <> 0) then begin
window := control^^.contrlOwner;
case part of
kInUpButtonControlPart, kInDownButtonControlPart: { a few pixels }
amount := 8;
kInPageUpControlPart, kInPageDownControlPart: { a page width }
with actionTE^^.viewRect do
amount := (right - left);
end;
if ((part = kInDownButtonControlPart) or (part = kInPageDownControlPart)) then
amount := -amount; { reverse direction }
CommonAction(control, amount);
if (amount <> 0) then
TEScroll(amount, 0, actionTE);
end;
end; { HActionProc }
{ Gets called from CallClickLoop which in turn }
{ is called by the TEClick toolbox routine. Saves the window's clip region, }
{ sets it to the portRect, adjusts the scrollbar values to match the TE scroll }
{ amount, then restores the clip region. }
procedure EditObject.ClickLoop;
var
region: RgnHandle;
vc, hc: integer;
begin
SetPort(window);
region := NewRgn;
GetClip(region); { save the old clip }
ClipRect(window^.portRect); { set the new clip }
vc := AdjustHV(true, vcontrol, te, false);
hc := AdjustHV(false, hcontrol, te, false);
SetClip(region); { restore the old clip }
DisposeRgn(region);
end; { PascalClikLoop }
function EditObject.WordBreak (text: Ptr; pos: integer; forward: boolean): boolean;
begin
if forward then
WordBreak := (pos > 0) and (Ptr(longint(text) + pos - 1)^ = 13)
else
WordBreak := Ptr(longint(text) + pos)^ = 13
end;
procedure EditObject.DoItemWhere (er: EventRecord; item: integer);
var
control: ControlHandle;
value, part: integer;
uss, use: integer;
begin
uss := te^^.selStart;
use := te^^.selEnd;
SetPort(window);
GlobalToLocal(er.where);
part := FindControl(er.where, window, control);
if part = 0 then begin
if PtInRect(er.where, te^^.viewRect) then
Click(er.where, BAND(er.modifiers, shiftKey) <> 0)
end
else begin
if part = kInIndicatorControlPart then begin
value := GetControlValue(control);
part := TrackControl(control, er.where, nil);
if part <> 0 then begin
value := value - GetControlValue(control);
if value <> 0 then
if control = vcontrol then
TEScroll(0, value * te^^.lineHeight, te)
else
TEScroll(value, 0, te);
end;
end
else begin
actionTE := te;
if control = vcontrol then
value := TrackControl(control, er.where, @VActionProc)
else
value := TrackControl(control, er.where, @HActionProc);
end;
end;
if (uss <> te^^.selStart) or (use <> te^^.selEnd) then
undoopen := false;
end;
function EditObject.EditMenuEnabled: boolean;
var
i: integer;
offset: longint;
begin
for i := EMundo to EMselectall do
if i <> EMundo + 1 then
SetEditMenuItem(i);
EditMenuEnabled := false;
if (te^^.selStart < te^^.selEnd) or (te^^.teLength > 0) then { Select All, Copy }
EditMenuEnabled := true;
if not readonly and ((undostart >= 0) or (GetScrap(nil, 'TEXT', offset) > 0)) then { Undo, Paste }
EditMenuEnabled := true;
end;
procedure EditObject.SetEditMenuItem (item: integer);
var
offset: longint;
begin
case item of
EMundo:
SetIDItemEnable(M_Edit, item, undostart >= 0);
EMcut, EMclear:
SetIDItemEnable(M_Edit, item, not readonly and (te^^.selStart < te^^.selEnd)); { Can cut,clear iff there is a selection and its not readonly}
EMcopy:
SetIDItemEnable(M_Edit, item, te^^.selStart < te^^.selEnd); { Can copy iff there is a selection }
EMpaste:
SetIDItemEnable(M_Edit, item, not readonly and (GetScrap(nil, 'TEXT', offset) > 0)); {Paste is enabled for app. windows}
EMselectall:
SetIDItemEnable(M_Edit, item, te^^.teLength > 0); { Can select all iff there is something to select }
otherwise
end;
end;
procedure CopyUndoSelection (h: Handle; te: TEHandle);
begin
SetHandleSize(h, te^^.selEnd - te^^.selStart);
BlockMove(Ptr(longint(te^^.hText^) + te^^.selStart), h^, te^^.selEnd - te^^.selStart);
end;
procedure EditObject.DoEditMenu (item: integer);
var
oe: OSErr;
loe: longint;
th: Handle;
uss, use: integer;
begin
undoopen := false;
case item of
EMcopy: begin
TECopy(te);
loe := ZeroScrap;
oe := TEToScrap;
end;
EMselectall: begin
SetPort(window);
TESetSelect(0, maxLongInt, te);
end;
EMcut: begin
CopyUndoSelection(undotext, te);
undoselstart := te^^.selStart;
undoselend := te^^.selEnd;
undostart := te^^.selStart;
undoend := undostart;
TECut(te);
loe := ZeroScrap;
oe := TEToScrap;
modified := true;
end;
EMclear: begin
CopyUndoSelection(undotext, te);
undoselstart := te^^.selStart;
undoselend := te^^.selEnd;
undostart := te^^.selStart;
undoend := undostart;
TEDelete(te);
modified := true;
end;
EMpaste: begin
oe := TEFromScrap;
if TEGetScrapLength + (te^^.teLength - (te^^.selEnd - te^^.selStart)) > 32000 then
AlertUser(paste_to_big)
else begin
CopyUndoSelection(undotext, te);
undoselstart := te^^.selStart;
undoselend := te^^.selEnd;
undostart := te^^.selStart;
TEPaste(te);
undoend := te^^.selEnd;
modified := true;
end;
end;
EMundo: begin
uss := undoselstart;
use := undoselend;
undoselstart := te^^.selStart;
undoselend := te^^.selEnd;
th := NewHandle(undoend - undostart);
BlockMove(Ptr(longint(te^^.hText^) + undostart), th^, undoend - undostart); { save undo for redo }
TESetSelect(undostart, undoend, te);
TEDelete(te);
HLock(undotext);
TEInsert(undotext^, GetHandleSize(undotext), te);
DisposeHandle(undotext);
undotext := th;
undoend := te^^.selEnd;
TESetSelect(uss, use, te);
end;
otherwise
end;
end;
procedure EditObject.DoIdle;
begin
if not readonly then
TEIdle(te);
end;
procedure EditObject.DoKey (modifiers: integer; ch: char);
procedure Doit;
begin
if BAND(modifiers, cmdKey) = 0 then
TEKey(ch, te);
Adjust;
end;
var
dk: boolean;
begin
dk := DirtyKey(ch);
if dk then begin
if not readonly then begin
modified := true;
if not undoopen then begin
CopyUndoSelection(undotext, te);
undoselstart := te^^.selStart;
undoselend := te^^.selEnd;
undostart := te^^.selStart;
end;
Doit;
undoend := te^^.selEnd;
end
else begin
SysBeep(1);
end;
end
else begin
Doit;
end;
undoopen := dk;
end;
end.